/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.configuration.tree;
import java.util.Iterator;
import java.util.List;
import junit.framework.TestCase;
/**
* Test class for DefaultExpressionEngine.
*
* @author Oliver Heger
*/
public class TestDefaultExpressionEngine extends TestCase
{
/** Stores the names of the test nodes representing tables. */
private static String[] tables =
{ "users", "documents"};
/** Stores the types of the test table nodes. */
private static String[] tabTypes =
{ "system", "application"};
/** Test data fields for the node hierarchy. */
private static String[][] fields =
{
{ "uid", "uname", "firstName", "lastName", "email"},
{ "docid", "name", "creationDate", "authorID", "version"}};
/** The object to be tested. */
DefaultExpressionEngine engine;
/** The root of a hierarchy with configuration nodes. */
ConfigurationNode root;
protected void setUp() throws Exception
{
super.setUp();
root = setUpNodes();
engine = new DefaultExpressionEngine();
}
/**
* Tests some simple queries.
*/
public void testQueryKeys()
{
checkKey("tables.table.name", "name", 2);
checkKey("tables.table.fields.field.name", "name", 10);
checkKey("tables.table[@type]", "type", 2);
checkKey("tables.table(0).fields.field.name", "name", 5);
checkKey("tables.table(1).fields.field.name", "name", 5);
checkKey("tables.table.fields.field(1).name", "name", 2);
}
/**
* Performs some queries and evaluates the values of the result nodes.
*/
public void testQueryNodes()
{
for (int i = 0; i < tables.length; i++)
{
checkKeyValue("tables.table(" + i + ").name", "name", tables[i]);
checkKeyValue("tables.table(" + i + ")[@type]", "type", tabTypes[i]);
for (int j = 0; j < fields[i].length; j++)
{
checkKeyValue("tables.table(" + i + ").fields.field(" + j
+ ").name", "name", fields[i][j]);
}
}
}
/**
* Tests querying keys that do not exist.
*/
public void testQueryNonExistingKeys()
{
checkKey("tables.tablespace.name", null, 0);
checkKey("tables.table(2).name", null, 0);
checkKey("a complete unknown key", null, 0);
checkKey("tables.table(0).fields.field(-1).name", null, 0);
checkKey("tables.table(0).fields.field(28).name", null, 0);
checkKey("tables.table(0).fields.field().name", null, 0);
checkKey("connection.settings.usr.name", null, 0);
}
/**
* Tests querying nodes whose names contain a delimiter.
*/
public void testQueryEscapedKeys()
{
checkKeyValue("connection..settings.usr..name", "usr.name", "scott");
checkKeyValue("connection..settings.usr..pwd", "usr.pwd", "tiger");
}
/**
* Tests some queries when the same delimiter is used for properties and
* attributes.
*/
public void testQueryAttributeEmulation()
{
engine.setAttributeEnd(null);
engine.setAttributeStart(engine.getPropertyDelimiter());
checkKeyValue("tables.table(0).name", "name", tables[0]);
checkKeyValue("tables.table(0).type", "type", tabTypes[0]);
checkKey("tables.table.type", "type", 2);
}
/**
* Tests accessing the root node.
*/
public void testQueryRootNode()
{
List nodes = checkKey(null, null, 1);
assertSame("Root node not found", root, nodes.get(0));
nodes = checkKey("", null, 1);
assertSame("Root node not found", root, nodes.get(0));
checkKeyValue("[@test]", "test", "true");
}
/**
* Tests a different query snytax. Sets other strings for the typical tokens
* used by the expression engine.
*/
public void testQueryAlternativeSyntax()
{
setUpAlternativeSyntax();
checkKeyValue("tables/table[1]/name", "name", tables[1]);
checkKeyValue("tables/table[0]@type", "type", tabTypes[0]);
checkKeyValue("@test", "test", "true");
checkKeyValue("connection.settings/usr.name", "usr.name", "scott");
}
/**
* Tests obtaining keys for nodes.
*/
public void testNodeKey()
{
ConfigurationNode node = root.getChild(0);
assertEquals("Invalid name for descendant of root", "tables", engine
.nodeKey(node, ""));
assertEquals("Parent key not respected", "test.tables", engine.nodeKey(
node, "test"));
assertEquals("Full parent key not taken into account",
"a.full.parent.key.tables", engine.nodeKey(node,
"a.full.parent.key"));
}
/**
* Tests obtaining keys when the root node is involved.
*/
public void testNodeKeyWithRoot()
{
assertEquals("Wrong name for root noot", "", engine.nodeKey(root, null));
assertEquals("Null name not detected", "test", engine.nodeKey(root,
"test"));
}
/**
* Tests obtaining keys for attribute nodes.
*/
public void testNodeKeyWithAttribute()
{
ConfigurationNode node = root.getChild(0).getChild(0).getAttribute(0);
assertEquals("Wrong attribute node", "type", node.getName());
assertEquals("Wrong attribute key", "tables.table[@type]", engine
.nodeKey(node, "tables.table"));
assertEquals("Wrong key for root attribute", "[@test]", engine.nodeKey(
root.getAttribute(0), ""));
}
/**
* Tests obtaining keys for nodes that contain the delimiter character.
*/
public void testNodeKeyWithEscapedDelimiters()
{
ConfigurationNode node = root.getChild(1);
assertEquals("Wrong escaped key", "connection..settings", engine
.nodeKey(node, ""));
assertEquals("Wrong complex escaped key",
"connection..settings.usr..name", engine.nodeKey(node
.getChild(0), engine.nodeKey(node, "")));
}
/**
* Tests obtaining node keys when a different syntax is set.
*/
public void testNodeKeyWithAlternativeSyntax()
{
setUpAlternativeSyntax();
assertEquals("Wrong child key", "tables/table", engine.nodeKey(root
.getChild(0).getChild(0), "tables"));
assertEquals("Wrong attribute key", "@test", engine.nodeKey(root
.getAttribute(0), ""));
engine.setAttributeStart(engine.getPropertyDelimiter());
assertEquals("Wrong attribute key", "/test", engine.nodeKey(root
.getAttribute(0), ""));
}
/**
* Tests adding direct child nodes to the existing hierarchy.
*/
public void testPrepareAddDirectly()
{
NodeAddData data = engine.prepareAdd(root, "newNode");
assertSame("Wrong parent node", root, data.getParent());
assertTrue("Path nodes available", data.getPathNodes().isEmpty());
assertEquals("Wrong name of new node", "newNode", data.getNewNodeName());
assertFalse("New node is an attribute", data.isAttribute());
data = engine.prepareAdd(root, "tables.table.fields.field.name");
assertEquals("Wrong name of new node", "name", data.getNewNodeName());
assertTrue("Path nodes available", data.getPathNodes().isEmpty());
assertEquals("Wrong parent node", "field", data.getParent().getName());
ConfigurationNode nd = data.getParent().getChild(0);
assertEquals("Field has no name node", "name", nd.getName());
assertEquals("Incorrect name", "version", nd.getValue());
}
/**
* Tests adding when indices are involved.
*/
public void testPrepareAddWithIndex()
{
NodeAddData data = engine
.prepareAdd(root, "tables.table(0).tableSpace");
assertEquals("Wrong name of new node", "tableSpace", data
.getNewNodeName());
assertTrue("Path nodes available", data.getPathNodes().isEmpty());
assertEquals("Wrong type of parent node", "table", data.getParent()
.getName());
ConfigurationNode node = data.getParent().getChild(0);
assertEquals("Wrong table", tables[0], node.getValue());
data = engine.prepareAdd(root, "tables.table(1).fields.field(2).alias");
assertEquals("Wrong name of new node", "alias", data.getNewNodeName());
assertEquals("Wrong type of parent node", "field", data.getParent()
.getName());
assertEquals("Wrong field node", "creationDate", data.getParent()
.getChild(0).getValue());
}
/**
* Tests adding new attributes.
*/
public void testPrepareAddAttribute()
{
NodeAddData data = engine.prepareAdd(root,
"tables.table(0)[@tableSpace]");
assertEquals("Wrong table node", tables[0], data.getParent()
.getChild(0).getValue());
assertEquals("Wrong name of new node", "tableSpace", data
.getNewNodeName());
assertTrue("Attribute not detected", data.isAttribute());
assertTrue("Path nodes available", data.getPathNodes().isEmpty());
data = engine.prepareAdd(root, "[@newAttr]");
assertSame("Root node is not parent", root, data.getParent());
assertEquals("Wrong name of new node", "newAttr", data.getNewNodeName());
assertTrue("Attribute not detected", data.isAttribute());
}
/**
* Tests add operations where complete pathes are added.
*/
public void testPrepareAddWithPath()
{
NodeAddData data = engine.prepareAdd(root,
"tables.table(1).fields.field(-1).name");
assertEquals("Wrong name of new node", "name", data.getNewNodeName());
checkNodePath(data, new String[]
{ "field"});
assertEquals("Wrong type of parent node", "fields", data.getParent()
.getName());
data = engine.prepareAdd(root, "tables.table(-1).name");
assertEquals("Wrong name of new node", "name", data.getNewNodeName());
checkNodePath(data, new String[]
{ "table"});
assertEquals("Wrong type of parent node", "tables", data.getParent()
.getName());
data = engine.prepareAdd(root, "a.complete.new.path");
assertEquals("Wrong name of new node", "path", data.getNewNodeName());
checkNodePath(data, new String[]
{ "a", "complete", "new"});
assertSame("Root is not parent", root, data.getParent());
}
/**
* Tests add operations when property and attribute delimiters are equal.
* Then it is not possible to add new attribute nodes.
*/
public void testPrepareAddWithSameAttributeDelimiter()
{
engine.setAttributeEnd(null);
engine.setAttributeStart(engine.getPropertyDelimiter());
NodeAddData data = engine.prepareAdd(root, "tables.table(0).test");
assertEquals("Wrong name of new node", "test", data.getNewNodeName());
assertFalse("New node is an attribute", data.isAttribute());
assertEquals("Wrong type of parent node", "table", data.getParent()
.getName());
data = engine.prepareAdd(root, "a.complete.new.path");
assertFalse("New node is an attribute", data.isAttribute());
checkNodePath(data, new String[]
{ "a", "complete", "new"});
}
/**
* Tests add operations when an alternative syntax is set.
*/
public void testPrepareAddWithAlternativeSyntax()
{
setUpAlternativeSyntax();
NodeAddData data = engine.prepareAdd(root, "tables/table[0]/test");
assertEquals("Wrong name of new node", "test", data.getNewNodeName());
assertFalse("New node is attribute", data.isAttribute());
assertEquals("Wrong parent node", tables[0], data.getParent().getChild(
0).getValue());
data = engine.prepareAdd(root, "a/complete/new/path@attr");
assertEquals("Wrong name of new attribute", "attr", data
.getNewNodeName());
checkNodePath(data, new String[]
{ "a", "complete", "new", "path"});
assertSame("Root is not parent", root, data.getParent());
}
/**
* Tests using invalid keys, e.g. if something should be added to
* attributes.
*/
public void testPrepareAddInvalidKeys()
{
try
{
engine.prepareAdd(root, "tables.table(0)[@type].new");
fail("Could add node to existing attribute!");
}
catch (IllegalArgumentException iex)
{
// ok
}
try
{
engine
.prepareAdd(root,
"a.complete.new.path.with.an[@attribute].at.a.non.allowed[@position]");
fail("Could add invalid path!");
}
catch (IllegalArgumentException iex)
{
// ok
}
try
{
engine.prepareAdd(root, null);
fail("Could add null key!");
}
catch (IllegalArgumentException iex)
{
// ok
}
try
{
engine.prepareAdd(root, "");
fail("Could add undefined key!");
}
catch (IllegalArgumentException iex)
{
// ok
}
}
/**
* Creates a node hierarchy for testing that consists of tables, their
* fields, and some additional data:
*
* <pre>
* tables
* table
* name
* fields
* field
* name
* field
* name
* </pre>
*
* @return the root of the test node hierarchy
*/
protected ConfigurationNode setUpNodes()
{
DefaultConfigurationNode rootNode = new DefaultConfigurationNode();
DefaultConfigurationNode nodeTables = new DefaultConfigurationNode(
"tables");
rootNode.addChild(nodeTables);
for (int i = 0; i < tables.length; i++)
{
DefaultConfigurationNode nodeTable = new DefaultConfigurationNode(
"table");
nodeTables.addChild(nodeTable);
nodeTable.addChild(new DefaultConfigurationNode("name", tables[i]));
nodeTable.addAttribute(new DefaultConfigurationNode("type",
tabTypes[i]));
DefaultConfigurationNode nodeFields = new DefaultConfigurationNode(
"fields");
nodeTable.addChild(nodeFields);
for (int j = 0; j < fields[i].length; j++)
{
nodeFields.addChild(createFieldNode(fields[i][j]));
}
}
DefaultConfigurationNode nodeConn = new DefaultConfigurationNode(
"connection.settings");
rootNode.addChild(nodeConn);
nodeConn.addChild(new DefaultConfigurationNode("usr.name", "scott"));
nodeConn.addChild(new DefaultConfigurationNode("usr.pwd", "tiger"));
rootNode.addAttribute(new DefaultConfigurationNode("test", "true"));
return rootNode;
}
/**
* Configures the expression engine to use a different syntax.
*/
private void setUpAlternativeSyntax()
{
engine.setAttributeEnd(null);
engine.setAttributeStart("@");
engine.setPropertyDelimiter("/");
engine.setEscapedDelimiter(null);
engine.setIndexStart("[");
engine.setIndexEnd("]");
}
/**
* Helper method for checking the evaluation of a key. Queries the
* expression engine and tests if the expected results are returned.
*
* @param key the key
* @param name the name of the nodes to be returned
* @param count the number of expected result nodes
* @return the list with the results of the query
*/
private List checkKey(String key, String name, int count)
{
List nodes = engine.query(root, key);
assertEquals("Wrong number of result nodes for key " + key, count,
nodes.size());
for (Iterator it = nodes.iterator(); it.hasNext();)
{
assertEquals("Wrong result node for key " + key, name,
((ConfigurationNode) it.next()).getName());
}
return nodes;
}
/**
* Helper method for checking the value of a node specified by the given
* key. This method evaluates the key and checks whether the resulting node
* has the expected value.
*
* @param key the key
* @param name the expected name of the result node
* @param value the expected value of the result node
*/
private void checkKeyValue(String key, String name, String value)
{
List nodes = checkKey(key, name, 1);
assertEquals("Wrong value for key " + key, value,
((ConfigurationNode) nodes.get(0)).getValue());
}
/**
* Helper method for checking the path of an add operation.
*
* @param data the add data object
* @param expected the expected path nodes
*/
private void checkNodePath(NodeAddData data, String[] expected)
{
assertEquals("Wrong number of path nodes", expected.length, data
.getPathNodes().size());
Iterator it = data.getPathNodes().iterator();
for (int i = 0; i < expected.length; i++)
{
assertEquals("Wrong path node " + i, expected[i], it.next());
}
}
/**
* Helper method for creating a field node with its children for the test
* node hierarchy.
*
* @param name the name of the field
* @return the field node
*/
private static ConfigurationNode createFieldNode(String name)
{
DefaultConfigurationNode nodeField = new DefaultConfigurationNode(
"field");
nodeField.addChild(new DefaultConfigurationNode("name", name));
return nodeField;
}
}